﻿using DynORM.Enums;
using DynORM.Interfaces;
using DynORM.Interfaces.BLLMapper.Enum;
using DynORM.Mappers;
using DynORM.ReferenceObjects;
using DynORM.ValueObjects;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace DynORM.BLLMapper.Helpers
{
    public class ObjectAndRowAux
    {
        private static ObjectAndRowAux instance_mssql;
        private static ObjectAndRowAux instance_postgresql;
        private static ObjectAndRowAux instance_mysql;
        private static ObjectAndRowAux instance_oracle;

        public static ObjectAndRowAux GetInstance(DBMSTypes type)
        {
            if (type == DBMSTypes.MSSQL)
            {
                if (instance_mssql == null)
                    instance_mssql = new ObjectAndRowAux(type);
                return instance_mssql;
            }
            else if (type == DBMSTypes.PostgreSQL)
            {
                if (instance_postgresql == null)
                    instance_postgresql = new ObjectAndRowAux(type);
                return instance_postgresql;
            }
            else if (type == DBMSTypes.MySQL)
            {
                if (instance_mysql == null)
                    instance_mysql = new ObjectAndRowAux(type);
                return instance_mysql;
            }
            else if (type == DBMSTypes.Oracle)
            {
                if (instance_oracle == null)
                    instance_oracle = new ObjectAndRowAux(type);
                return instance_oracle;
            }
            else
                throw new Exception("DBMS type is not supported.");
        }

        private DBMSTypes dbmsType;

        private ObjectAndRowAux(DBMSTypes dbmsType)
        {
            if (dbmsType == DBMSTypes.MSSQL || dbmsType == DBMSTypes.PostgreSQL || dbmsType == DBMSTypes.MySQL || dbmsType == DBMSTypes.Oracle)
                this.dbmsType = dbmsType;
            else
                throw new Exception("DBMS type is not supported.");
        }




        //in read logic
        //discover Type using custom attributes and init attributed members with values from IRow
        //here we assume that values of PKs and Fields represent "entity" and values of FKs represent "relations".
        //if we load lazy we load only "entity" and all the child objects do not loaded ("relations"). 
        //otherwise we load "entity" and "relation" and child's "entity" (and recreate instance of parent and child objects).
        //the same we do when we upload object. 
        //when we upload parent we load parent's "relations" and child's "entity" (and recreate child objects out of them). 
        //in case of deep upload we load child object fully (no lazy loading).
        //otherwise we use lazy loading when upload children.


        //in write logic
        //here we assume that values of PKs and Fields represent "entity" and values of FKs represent "relations".
        //using object's Type we discover members and get its values and recreate with them IRow which is stored in db
        //we store separatly "entity" and "relations" of this "entity"'s children and children's "enitites". 
        //in case of processing batch of object first we store all the "entities" and than we store all "relations" in order to avoid broken references.



        private static void SubstituteChildInObjOneToMany(object parent, object newchild, object oldchild)
        {
            IEnumerable<FieldInfo> fieldInfos = parent.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance);
            foreach (FieldInfo fi in fieldInfos)
            {
                Type t;
                if (fi.FieldType.GetInterfaces().Any<Type>(i => i == typeof(IList))
                    && fi.FieldType.GetInterfaces().Any<Type>(i => i == typeof(IEnumerable))
                    && fi.FieldType.IsGenericType)
                {
                    t = fi.FieldType.GetGenericArguments().First<Type>();
                    IEnumerable<ForeignKeyDeclarationAttribute> attrs = t.GetCustomAttributes(typeof(ForeignKeyDeclarationAttribute), true).Select<object, ForeignKeyDeclarationAttribute>(a => (ForeignKeyDeclarationAttribute)a);
                    ForeignKeyDeclarationAttribute attr = attrs.FirstOrDefault<ForeignKeyDeclarationAttribute>(attr1 => attr1.ForeignType == parent.GetType() && attr1.MemberName == fi.Name && attr1.RelationQuantity != RelationQuantity.ManyOrZeroToManyOrZero);
                    if (attr != null)
                    {
                        MethodInfo getEnumeratorMethod = fi.FieldType.GetMethods().Where(m => m.Name == "GetEnumerator" && m.GetParameters().Count() == 0).First();
                        IEnumerator enumerator = (IEnumerator)getEnumeratorMethod.Invoke(fi.GetValue(parent), new object[0]);
                        bool found = false;
                        while (enumerator.MoveNext())
                            if (Object.ReferenceEquals(enumerator.Current, oldchild))
                            {
                                found = true;
                                break; //cannot remove while iterator is active (get an exception), so break the cycle
                            }

                        if (found)
                        {
                            MethodInfo addMethod = fi.FieldType.GetMethods().Where(m => m.Name == "Add" && m.GetParameters().Count() == 1).FirstOrDefault();
                            addMethod.Invoke(fi.GetValue(parent), new object[] { newchild });

                            MethodInfo removeMethod = fi.FieldType.GetMethods().Where(m => m.Name == "Remove" && m.GetParameters().Count() == 1).FirstOrDefault();
                            removeMethod.Invoke(fi.GetValue(parent), new object[] { oldchild });
                        }
                    }
                }
                else
                {
                    t = fi.FieldType;
                    IEnumerable<ForeignKeyDeclarationAttribute> attrs = t.GetCustomAttributes(typeof(ForeignKeyDeclarationAttribute), true).Select<object, ForeignKeyDeclarationAttribute>(a => (ForeignKeyDeclarationAttribute)a);
                    ForeignKeyDeclarationAttribute attr = attrs.FirstOrDefault<ForeignKeyDeclarationAttribute>(attr1 => attr1.ForeignType == parent.GetType() && attr1.MemberName == fi.Name && attr1.RelationQuantity != RelationQuantity.ManyOrZeroToManyOrZero);
                    if (attr != null)
                    {
                        if (Object.ReferenceEquals(fi.GetValue(parent), oldchild))
                            fi.SetValue(parent, newchild);
                    }
                }
            }

            IEnumerable<PropertyInfo> propertyInfos = parent.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
            foreach (PropertyInfo pi in propertyInfos)
                if (pi.CanRead && pi.CanWrite)
                {
                    Type t;
                    if (pi.PropertyType.GetInterfaces().Any<Type>(i => i == typeof(IList))
                        && pi.PropertyType.GetInterfaces().Any<Type>(i => i == typeof(IEnumerable))
                        && pi.PropertyType.IsGenericType)
                    {
                        t = pi.PropertyType.GetGenericArguments().First<Type>();
                        IEnumerable<ForeignKeyDeclarationAttribute> attrs = t.GetCustomAttributes(typeof(ForeignKeyDeclarationAttribute), true).Select<object, ForeignKeyDeclarationAttribute>(a => (ForeignKeyDeclarationAttribute)a);
                        ForeignKeyDeclarationAttribute attr = attrs.FirstOrDefault<ForeignKeyDeclarationAttribute>(attr1 => attr1.ForeignType == parent.GetType() && attr1.MemberName == pi.Name && attr1.RelationQuantity != RelationQuantity.ManyOrZeroToManyOrZero);
                        if (attr != null)
                        {
                            MethodInfo getEnumeratorMethod = pi.PropertyType.GetMethods().Where(m => m.Name == "GetEnumerator" && m.GetParameters().Count() == 0).First();
                            IEnumerator enumerator = (IEnumerator)getEnumeratorMethod.Invoke(pi.GetValue(parent, null), new object[0]);
                            bool found = false;
                            while (enumerator.MoveNext())
                                if (Object.ReferenceEquals(enumerator.Current, oldchild))
                                {
                                    found = true;
                                    break; //cannot remove while iterator is active (get an exception), so break the cycle
                                }

                            if (found)
                            {
                                MethodInfo addMethod = pi.PropertyType.GetMethods().Where(m => m.Name == "Add" && m.GetParameters().Count() == 1).FirstOrDefault();
                                addMethod.Invoke(pi.GetValue(parent, null), new object[] { newchild });

                                MethodInfo removeMethod = pi.PropertyType.GetMethods().Where(m => m.Name == "Remove" && m.GetParameters().Count() == 1).FirstOrDefault();
                                removeMethod.Invoke(pi.GetValue(parent, null), new object[] { oldchild });
                            }
                        }
                    }
                    else
                    {
                        t = pi.PropertyType;
                        IEnumerable<ForeignKeyDeclarationAttribute> attrs = t.GetCustomAttributes(typeof(ForeignKeyDeclarationAttribute), true).Select<object, ForeignKeyDeclarationAttribute>(a => (ForeignKeyDeclarationAttribute)a);
                        ForeignKeyDeclarationAttribute attr = attrs.FirstOrDefault<ForeignKeyDeclarationAttribute>(attr1 => attr1.ForeignType == parent.GetType() && attr1.MemberName == pi.Name && attr1.RelationQuantity != RelationQuantity.ManyOrZeroToManyOrZero);
                        if (attr != null)
                        {
                            if (Object.ReferenceEquals(pi.GetValue(parent, null), oldchild))
                                pi.SetValue(parent, newchild, null);
                        }
                    }
                }
        }

        private static void SubstituteChildInObjManyToMany(object parent, object newchild, object oldchild)
        {
            //get children from many-to-many relations
            IEnumerable<ForeignKeyDeclarationAttribute> mtmattrs =
                parent.GetType().GetCustomAttributes(typeof(ForeignKeyDeclarationAttribute), true)
                    .Select<object, ForeignKeyDeclarationAttribute>(a => (ForeignKeyDeclarationAttribute)a)
                    .Where<ForeignKeyDeclarationAttribute>(a => a.RelationQuantity == RelationQuantity.ManyOrZeroToManyOrZero);

            foreach (ForeignKeyDeclarationAttribute attr in mtmattrs)
            {
                //get parent's member in which children are stored
                PropertyInfo propertyInfo = parent.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)
                    .Where<PropertyInfo>(pi => pi.CanRead && pi.CanWrite && String.Compare(pi.Name, attr.MemberName, StringComparison.OrdinalIgnoreCase) == 0)
                    .FirstOrDefault();

                FieldInfo fieldInfo = parent.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance)
                    .Where<FieldInfo>(fi => String.Compare(fi.Name, attr.MemberName, StringComparison.OrdinalIgnoreCase) == 0)
                    .FirstOrDefault();

                //retrieve children from object
                if (fieldInfo != null)
                {
                    MethodInfo getEnumeratorMethod = fieldInfo.FieldType.GetMethods().Where(m => m.Name == "GetEnumerator" && m.GetParameters().Count() == 0).First();
                    IEnumerator enumerator = (IEnumerator)getEnumeratorMethod.Invoke(fieldInfo.GetValue(parent), new object[0]);
                    bool found = false;
                    while (enumerator.MoveNext())
                        if (Object.ReferenceEquals(enumerator.Current, oldchild))
                        {
                            found = true;
                            break; //cannot remove while iterator is active (get an exception), so break the cycle
                        }

                    if (found)
                    {
                        MethodInfo addMethod = fieldInfo.FieldType.GetMethods().Where(m => m.Name == "Add" && m.GetParameters().Count() == 1).FirstOrDefault();
                        addMethod.Invoke(fieldInfo.GetValue(parent), new object[] { newchild });

                        MethodInfo removeMethod = fieldInfo.FieldType.GetMethods().Where(m => m.Name == "Remove" && m.GetParameters().Count() == 1).FirstOrDefault();
                        removeMethod.Invoke(fieldInfo.GetValue(parent), new object[] { oldchild });
                    }
                }
                else if (propertyInfo != null)
                {
                    MethodInfo getEnumeratorMethod = propertyInfo.PropertyType.GetMethods().Where(m => m.Name == "GetEnumerator" && m.GetParameters().Count() == 0).First();
                    IEnumerator enumerator = (IEnumerator)getEnumeratorMethod.Invoke(propertyInfo.GetValue(parent, null), new object[0]);
                    bool found = false;
                    while (enumerator.MoveNext())
                        if (Object.ReferenceEquals(enumerator.Current, oldchild))
                        {
                            found = true;
                            break; //cannot remove while iterator is active (get an exception), so break the cycle
                        }

                    if (found)
                    {
                        MethodInfo addMethod = propertyInfo.PropertyType.GetMethods().Where(m => m.Name == "Add" && m.GetParameters().Count() == 1).FirstOrDefault();
                        addMethod.Invoke(propertyInfo.GetValue(parent, null), new object[] { newchild });

                        MethodInfo removeMethod = propertyInfo.PropertyType.GetMethods().Where(m => m.Name == "Remove" && m.GetParameters().Count() == 1).FirstOrDefault();
                        removeMethod.Invoke(propertyInfo.GetValue(parent, null), new object[] { oldchild });
                    }
                }
            }
        }



        public static IPrimaryKey GetBLLObjPrimaryKey(Type bllObjType, params object[] bllObjIDs)
        {
            IPrimaryKey result;
            string tableName = TypeAndRowDescriptorAux.GetBLLObjTypeTableName(bllObjType);
            string name = String.Format("{0}_PK", tableName);
            Dictionary<int, IField> fields = new Dictionary<int, IField>();
            foreach (KeyValuePair<int, MemberInfo> kv in TypeAndRowDescriptorAux.GetBLLObjTypePrimaryKeyMemberInfos(bllObjType).OrderBy(kv1 => kv1.Key))
            {
                PrimaryKeyAttribute pka = (PrimaryKeyAttribute)kv.Value.GetCustomAttributes(typeof(PrimaryKeyAttribute), true).First();
                string fieldName = String.IsNullOrEmpty(pka.FieldName) ? kv.Value.Name : pka.FieldName;
                if (kv.Value is FieldInfo)
                {
                    FieldInfo fi = kv.Value as FieldInfo;
                    fields.Add(kv.Key, new Field(fi.FieldType, fieldName, bllObjIDs[kv.Key]));
                }
                if (kv.Value is PropertyInfo)
                {
                    PropertyInfo pi = kv.Value as PropertyInfo;
                    fields.Add(kv.Key, new Field(pi.PropertyType, fieldName, bllObjIDs[kv.Key]));
                }
            }
            result = new PrimaryKey(tableName, fields, name);
            return result;
        }

        public static IForeignKey GetCorrespondingForeignKey(IPrimaryKey primaryKey1, IPrimaryKey primaryKey2, int fkOrd)
        {
            IForeignKey result;

            IDictionary<int, IField> fields = new Dictionary<int, IField>();
            foreach (KeyValuePair<int, IField> kv in primaryKey1.Fields)
                fields.Add(kv.Key, new Field(kv.Value.FieldType, String.Format("{0}_{1}_{2}_{3}_{4}", primaryKey1.TableName, primaryKey2.TableName, ForeignKeyDescriptor.PredefinedFieldNameForForeignIDs, fkOrd, kv.Key), kv.Value.FieldValue));
            string name = String.Format("{0}_{1}_FK_{2}", primaryKey1.TableName, primaryKey2.TableName, fkOrd);
            result = new ForeignKey(primaryKey1.TableName, fields, primaryKey2.TableName, primaryKey2.Fields, name);

            return result;
        }

        public static void SubstituteChildInObj(object parent, object newchild, object oldchild)
        {
            SubstituteChildInObjOneToMany(parent, newchild, oldchild);
            SubstituteChildInObjManyToMany(parent, newchild, oldchild);
        }

        public static void InitBLLObjPrimaryKey(object bllObj, IRow row)
        {
        //this method is used for "entity" loading
            foreach (KeyValuePair<int, MemberInfo> kv in TypeAndRowDescriptorAux.GetBLLObjTypePrimaryKeyMemberInfos(bllObj.GetType()))
            {
                PrimaryKeyAttribute pka = (PrimaryKeyAttribute)kv.Value.GetCustomAttributes(typeof(PrimaryKeyAttribute), true).First();
                string fieldName = String.IsNullOrEmpty(pka.FieldName) ? kv.Value.Name : pka.FieldName;
                IField f = row.PrimaryKey.Fields[kv.Key];

                if (kv.Value is FieldInfo)
                {
                    FieldInfo fi = kv.Value as FieldInfo;
                    fi.SetValue(bllObj, f.FieldValue);
                }
                if (kv.Value is PropertyInfo)
                {
                    PropertyInfo pi = kv.Value as PropertyInfo;
                    pi.SetValue(bllObj, f.FieldValue, null);
                }
            }
        }

        public static void InitBLLObjFields(object bllObj, IRow row)
        {
        //this method is used for "entity" loading
            foreach (MemberInfo mi in TypeAndRowDescriptorAux.GetBLLObjTypeFieldsMemberInfos(bllObj.GetType()))
            {
                FieldAttribute fa = (FieldAttribute)mi.GetCustomAttributes(typeof(FieldAttribute), true).First();
                string fieldName = String.IsNullOrEmpty(fa.FieldName) ? mi.Name : fa.FieldName;
                //init only those fields which were found in row (some of them may be not even created in db, but domain type is updated)
                IField f = row.Fields.FirstOrDefault<IField>(f1 => String.Compare(fieldName, f1.FieldName, StringComparison.InvariantCultureIgnoreCase) == 0);
                if (f == null)
                    continue;

                if (mi is FieldInfo)
                {
                    FieldInfo fi = mi as FieldInfo;
                    fi.SetValue(bllObj, f.FieldValue);
                }
                if (mi is PropertyInfo)
                {
                    PropertyInfo pi = mi as PropertyInfo;
                    pi.SetValue(bllObj, f.FieldValue, null);
                }
            }
        }

        public static bool IsObjTypeChild(Type bllObjType)
        {
            IEnumerable<ForeignKeyDeclarationAttribute> fkdas = bllObjType.GetCustomAttributes(typeof(ForeignKeyDeclarationAttribute), true).Select<object, ForeignKeyDeclarationAttribute>(item => (ForeignKeyDeclarationAttribute)item);
            if (fkdas.Where<ForeignKeyDeclarationAttribute>(fkda => fkda.RelationQuantity != RelationQuantity.ManyOrZeroToManyOrZero).Count() > 0)
                return true;
            else
                return false;
        }

        public static bool IsObjTypeParent(Type bllObjType)
        {
            IEnumerable<ForeignKeyDeclarationAttribute> fkdas = bllObjType.GetCustomAttributes(typeof(ForeignKeyDeclarationAttribute), true).Select<object, ForeignKeyDeclarationAttribute>(item => (ForeignKeyDeclarationAttribute)item);
            if (fkdas.Where<ForeignKeyDeclarationAttribute>(fkda => fkda.RelationQuantity == RelationQuantity.ManyOrZeroToManyOrZero).Count() > 0)
                return true;

            IEnumerable<FieldInfo> fieldInfos = bllObjType.GetFields(BindingFlags.Public | BindingFlags.Instance);
            foreach (FieldInfo fi in fieldInfos)
            {
                Type t;
                if (fi.FieldType.GetInterfaces().Any<Type>(i => i == typeof(IList))
                    && fi.FieldType.GetInterfaces().Any<Type>(i => i == typeof(IEnumerable))
                    && fi.FieldType.IsGenericType)
                {
                    t = fi.FieldType.GetGenericArguments().First<Type>();
                    IEnumerable<ForeignKeyDeclarationAttribute> attrs = t.GetCustomAttributes(typeof(ForeignKeyDeclarationAttribute), true).Select<object, ForeignKeyDeclarationAttribute>(a => (ForeignKeyDeclarationAttribute)a);
                    if (attrs.Any<ForeignKeyDeclarationAttribute>(attr => attr.ForeignType == bllObjType && attr.MemberName == fi.Name))
                        return true;
                }
                else
                {
                    t = fi.FieldType;
                    IEnumerable<ForeignKeyDeclarationAttribute> attrs = t.GetCustomAttributes(typeof(ForeignKeyDeclarationAttribute), true).Select<object, ForeignKeyDeclarationAttribute>(a => (ForeignKeyDeclarationAttribute)a);
                    if (attrs.Any<ForeignKeyDeclarationAttribute>(attr => attr.ForeignType == bllObjType && attr.MemberName == fi.Name))
                        return true;
                }
            }

            IEnumerable<PropertyInfo> propertyInfos = bllObjType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
            foreach (PropertyInfo pi in propertyInfos)
                if (pi.CanRead && pi.CanWrite)
                {
                    Type t;
                    if (pi.PropertyType.GetInterfaces().Any<Type>(i => i == typeof(IList))
                        && pi.PropertyType.GetInterfaces().Any<Type>(i => i == typeof(IEnumerable))
                        && pi.PropertyType.IsGenericType)
                    {
                        t = pi.PropertyType.GetGenericArguments().First<Type>();
                        IEnumerable<ForeignKeyDeclarationAttribute> attrs = t.GetCustomAttributes(typeof(ForeignKeyDeclarationAttribute), true).Select<object, ForeignKeyDeclarationAttribute>(a => (ForeignKeyDeclarationAttribute)a);
                        if (attrs.Any<ForeignKeyDeclarationAttribute>(attr => attr.ForeignType == bllObjType && attr.MemberName == pi.Name))
                            return true;
                    }
                    else
                    {
                        t = pi.PropertyType;
                        IEnumerable<ForeignKeyDeclarationAttribute> attrs = t.GetCustomAttributes(typeof(ForeignKeyDeclarationAttribute), true).Select<object, ForeignKeyDeclarationAttribute>(a => (ForeignKeyDeclarationAttribute)a);
                        if (attrs.Any<ForeignKeyDeclarationAttribute>(attr => attr.ForeignType == bllObjType && attr.MemberName == pi.Name))
                            return true;
                    }
                }

            return false;
        }

        public static IEnumerable<Tuple<int, object, RelationQuantity>> GetOneToManyChildsFromObj(object parent)
        {
            List<Tuple<int, object, RelationQuantity>> result = new List<Tuple<int, object, RelationQuantity>>();

            IEnumerable<FieldInfo> fieldInfos = parent.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance);
            foreach (FieldInfo fi in fieldInfos)
            {
                Type t;
                if (fi.FieldType.GetInterfaces().Any<Type>(i => i == typeof(IList))
                    && fi.FieldType.GetInterfaces().Any<Type>(i => i == typeof(IEnumerable))
                    && fi.FieldType.IsGenericType)
                {
                    MethodInfo getEnumeratorMethod = fi.FieldType.GetMethods().Where(m => m.Name == "GetEnumerator" && m.GetParameters().Count() == 0).First();
                    IEnumerator enumerator = (IEnumerator)getEnumeratorMethod.Invoke(fi.GetValue(parent), new object[0]);
                    while (enumerator.MoveNext())
                    {
                        t = enumerator.Current.GetType();
                        //t = fi.FieldType.GetGenericArguments().First<Type>();
                        IEnumerable<ForeignKeyDeclarationAttribute> attrs = t.GetCustomAttributes(typeof(ForeignKeyDeclarationAttribute), true).Select<object, ForeignKeyDeclarationAttribute>(a => (ForeignKeyDeclarationAttribute)a);
                        ForeignKeyDeclarationAttribute attr = attrs.FirstOrDefault<ForeignKeyDeclarationAttribute>(attr1 => attr1.ForeignType == parent.GetType() && attr1.MemberName == fi.Name && attr1.RelationQuantity != RelationQuantity.ManyOrZeroToManyOrZero);
                        if (attr != null)
                            result.Add(new Tuple<int, object, RelationQuantity>(attr.Ord, enumerator.Current, attr.RelationQuantity));
                    }
                }
                else
                {
                    if (!Object.ReferenceEquals(fi.GetValue(parent), null))
                    {
                        //t = fi.FieldType;
                        t = fi.GetValue(parent).GetType();
                        IEnumerable<ForeignKeyDeclarationAttribute> attrs = t.GetCustomAttributes(typeof(ForeignKeyDeclarationAttribute), true).Select<object, ForeignKeyDeclarationAttribute>(a => (ForeignKeyDeclarationAttribute)a);
                        ForeignKeyDeclarationAttribute attr = attrs.FirstOrDefault<ForeignKeyDeclarationAttribute>(attr1 => attr1.ForeignType == parent.GetType() && attr1.MemberName == fi.Name && attr1.RelationQuantity != RelationQuantity.ManyOrZeroToManyOrZero);
                        if (attr != null)
                            result.Add(new Tuple<int, object, RelationQuantity>(attr.Ord, fi.GetValue(parent), attr.RelationQuantity));
                    }
                }
            }

            IEnumerable<PropertyInfo> propertyInfos = parent.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
            foreach (PropertyInfo pi in propertyInfos)
                if (pi.CanRead && pi.CanWrite)
                {
                    Type t;
                    if (pi.PropertyType.GetInterfaces().Any<Type>(i => i == typeof(IList))
                        && pi.PropertyType.GetInterfaces().Any<Type>(i => i == typeof(IEnumerable))
                        && pi.PropertyType.IsGenericType)
                    {
                        MethodInfo getEnumeratorMethod = pi.PropertyType.GetMethods().Where(m => m.Name == "GetEnumerator" && m.GetParameters().Count() == 0).First();
                        IEnumerator enumerator = (IEnumerator)getEnumeratorMethod.Invoke(pi.GetValue(parent, null), new object[0]);
                        while (enumerator.MoveNext())
                        {
                            t = enumerator.Current.GetType();
                            //t = pi.PropertyType.GetGenericArguments().First<Type>();
                            IEnumerable<ForeignKeyDeclarationAttribute> attrs = t.GetCustomAttributes(typeof(ForeignKeyDeclarationAttribute), true).Select<object, ForeignKeyDeclarationAttribute>(a => (ForeignKeyDeclarationAttribute)a);
                            ForeignKeyDeclarationAttribute attr = attrs.FirstOrDefault<ForeignKeyDeclarationAttribute>(attr1 => attr1.ForeignType == parent.GetType() && attr1.MemberName == pi.Name && attr1.RelationQuantity != RelationQuantity.ManyOrZeroToManyOrZero);
                            if (attr != null)
                                result.Add(new Tuple<int, object, RelationQuantity>(attr.Ord, enumerator.Current, attr.RelationQuantity));
                        }
                    }
                    else
                    {
                        //t = pi.PropertyType;
                        if (!Object.ReferenceEquals(pi.GetValue(parent, null), null))
                        {
                            t = pi.GetValue(parent, null).GetType();
                            IEnumerable<ForeignKeyDeclarationAttribute> attrs = t.GetCustomAttributes(typeof(ForeignKeyDeclarationAttribute), true).Select<object, ForeignKeyDeclarationAttribute>(a => (ForeignKeyDeclarationAttribute)a);
                            ForeignKeyDeclarationAttribute attr = attrs.FirstOrDefault<ForeignKeyDeclarationAttribute>(attr1 => attr1.ForeignType == parent.GetType() && attr1.MemberName == pi.Name && attr1.RelationQuantity != RelationQuantity.ManyOrZeroToManyOrZero);
                            if (attr != null)
                                result.Add(new Tuple<int, object, RelationQuantity>(attr.Ord, pi.GetValue(parent, null), attr.RelationQuantity));
                        }
                    }
                }

            return result;
        }

        public static IEnumerable<Tuple<int, object, RelationQuantity>> GetManyToManyChildsFromObj(object parent)
        {
            List<Tuple<int, object, RelationQuantity>> result = new List<Tuple<int, object, RelationQuantity>>();

            //get children from many-to-many relations
            IEnumerable<ForeignKeyDeclarationAttribute> mtmattrs =
                parent.GetType().GetCustomAttributes(typeof(ForeignKeyDeclarationAttribute), true)
                    .Select<object, ForeignKeyDeclarationAttribute>(a => (ForeignKeyDeclarationAttribute)a)
                    .Where<ForeignKeyDeclarationAttribute>(a => a.RelationQuantity == RelationQuantity.ManyOrZeroToManyOrZero);

            foreach (ForeignKeyDeclarationAttribute attr in mtmattrs)
            {
                //get parent's member in which children are stored
                PropertyInfo propertyInfo = parent.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)
                    .Where<PropertyInfo>(pi => pi.CanRead && pi.CanWrite && String.Compare(pi.Name, attr.MemberName, StringComparison.OrdinalIgnoreCase) == 0)
                    .FirstOrDefault();

                FieldInfo fieldInfo = parent.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance)
                    .Where<FieldInfo>(fi => String.Compare(fi.Name, attr.MemberName, StringComparison.OrdinalIgnoreCase) == 0)
                    .FirstOrDefault();

                //retrieve children from object
                if (fieldInfo != null)
                {
                    MethodInfo getEnumeratorMethod = fieldInfo.FieldType.GetMethods().Where(m => m.Name == "GetEnumerator" && m.GetParameters().Count() == 0).First();
                    IEnumerator enumerator = (IEnumerator)getEnumeratorMethod.Invoke(fieldInfo.GetValue(parent), new object[0]);
                    while (enumerator.MoveNext())
                        result.Add(new Tuple<int, object, RelationQuantity>(attr.Ord, enumerator.Current, attr.RelationQuantity));
                }
                else if (propertyInfo != null)
                {
                    MethodInfo getEnumeratorMethod = propertyInfo.PropertyType.GetMethods().Where(m => m.Name == "GetEnumerator" && m.GetParameters().Count() == 0).First();
                    IEnumerator enumerator = (IEnumerator)getEnumeratorMethod.Invoke(propertyInfo.GetValue(parent, null), new object[0]);
                    while (enumerator.MoveNext())
                        result.Add(new Tuple<int, object, RelationQuantity>(attr.Ord, enumerator.Current, attr.RelationQuantity));
                }
            }

            return result;
        }



        private IEnumerable<object> RetrieveOneToManyFKObjsFromDB(string connectionString, object parent, Type childType, MemberInfo mi)
        {
        //this method is used to upload in object "relations" and child's "entities" (upload of only the first layer of children)
            List<object> result = new List<object>();
            IEnumerable<ForeignKeyDeclarationAttribute> attrs = childType.GetCustomAttributes(typeof(ForeignKeyDeclarationAttribute), true).Select<object, ForeignKeyDeclarationAttribute>(a => (ForeignKeyDeclarationAttribute)a);
            ForeignKeyDeclarationAttribute fkda = attrs.FirstOrDefault<ForeignKeyDeclarationAttribute>(attr => attr.ForeignType == parent.GetType() && attr.MemberName == mi.Name);
            if (fkda.RelationQuantity != RelationQuantity.ManyOrZeroToManyOrZero)
            {
                IPrimaryKey pk = GetPrimaryKeyFromObj(parent);
                List<IORMComparison> predicate = new List<IORMComparison>();
                foreach (KeyValuePair<int, IField> kv in pk.Fields)
                    predicate.Add(new Comparison(String.Format("{0}_{1}_{2}_{3}_{4}", TypeAndRowDescriptorAux.GetBLLObjTypeTableName(childType), TypeAndRowDescriptorAux.GetBLLObjTypeTableName(parent.GetType()), ForeignKeyDescriptor.PredefinedFieldNameForForeignIDs, fkda.Ord, kv.Key), ORMComparisonOperator.Equal, kv.Value.FieldValue));

                IEnumerable<IRow> childrenRows = RowMapper.RowsRead(connectionString, this.dbmsType, TypeAndRowDescriptorAux.GetBLLObjTypeTableName(childType), predicate);
                if (!Object.ReferenceEquals(childrenRows, null))
                    foreach (IRow childRow in childrenRows)
                        result.Add(InitPKsandFields(childType, childRow, true));
            }
            else
                throw new Exception("RetrieveOneToManyFKObjsFromDB() is used to process many-to-many relationship. Use RetrieveManyToManyFKObjsFromDB() instead.");
            return result;
        }

        private IEnumerable<object> RetrieveManyToManyFKObjsFromDB(string connectionString, object parent, Type childType, MemberInfo mi, int ord)
        {
            //this method is used to upload in object "relations" and child's "entities" (upload of only the first layer of children)
            List<object> result = new List<object>();

            IPrimaryKey parent_pk = GetPrimaryKeyFromObj(parent);
            //retrieve records from link-table
            string linkTableName = String.Format("{0}_{1}_{2}", TypeAndRowDescriptorAux.GetBLLObjTypeTableName(parent.GetType()), TypeAndRowDescriptorAux.GetBLLObjTypeTableName(childType), ord);
            if (RowDescriptorMapper.IsExistsInDB(linkTableName, connectionString, this.dbmsType) == false)
            {
                linkTableName = String.Format("{0}_{1}_{2}", TypeAndRowDescriptorAux.GetBLLObjTypeTableName(childType), TypeAndRowDescriptorAux.GetBLLObjTypeTableName(parent.GetType()), ord);
                if (RowDescriptorMapper.IsExistsInDB(linkTableName, connectionString, this.dbmsType) == false)
                    return result;
            }
            List<IORMComparison> predicate = new List<IORMComparison>();
            foreach (KeyValuePair<int, IField> kv in parent_pk.Fields)
                predicate.Add(new Comparison(String.Format("{0}_{1}_{2}_{3}", linkTableName, TypeAndRowDescriptorAux.GetBLLObjTypeTableName(parent.GetType()), ForeignKeyDescriptor.PredefinedFieldNameForForeignIDs, kv.Key), ORMComparisonOperator.Equal, kv.Value.FieldValue));

            IEnumerable<IRow> linkTableRows = RowMapper.RowsRead(connectionString, this.dbmsType, linkTableName, predicate);
            //retrieve records from table to which we linked through link-table
            foreach (IRow linkTableRow in linkTableRows)
            {
                List<IORMComparison> predicate1 = new List<IORMComparison>();
                foreach (KeyValuePair<int, IField> kv in linkTableRow.ForeignKeys.First<IForeignKey>(fk => String.Compare(fk.Name, String.Format("{0}_{1}_FK", linkTableName, TypeAndRowDescriptorAux.GetBLLObjTypeTableName(childType)), StringComparison.OrdinalIgnoreCase) == 0).RelatedFields)
                    predicate1.Add(new Comparison(kv.Value.FieldName, ORMComparisonOperator.Equal, kv.Value.FieldValue));

                IRow relatedRow = RowMapper.RowsRead(connectionString, this.dbmsType, TypeAndRowDescriptorAux.GetBLLObjTypeTableName(childType), predicate1).First<IRow>();
                result.Add(InitPKsandFields(childType, relatedRow, true));
            }

            return result;
        }

        //use the dictionary in order to be sure we have objects with the same identity (only one instance) for every objects with the equal PK
        //we use it here as identity map for the first layer of choldren. 
        //similar dictionary we use in BLLAdaptiveMapper for check child's identity from the all layers (children's children and so on)
        private Dictionary<IPrimaryKey, object> instanciatedObjects = new Dictionary<IPrimaryKey, object>();

        private void InitBLLObjOneToManyFKObjects(string connectionString, object parent)
        {
            IEnumerable<FieldInfo> fieldInfos = parent.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance);
            foreach (FieldInfo fi in fieldInfos)
            {
                Type t2;
                if (fi.FieldType.GetInterfaces().Any<Type>(i => i == typeof(IList))
                    && fi.FieldType.GetInterfaces().Any<Type>(i => i == typeof(IEnumerable))
                    && fi.FieldType.IsGenericType)
                {
                    t2 = fi.FieldType.GetGenericArguments().First<Type>();

                    IEnumerable<Type> ts = parent.GetType().Assembly.GetTypes().Where<Type>(t1 => t2.IsAssignableFrom(t1));
                    foreach (Type t in ts)
                    {
                        IEnumerable<ForeignKeyDeclarationAttribute> attrs = t.GetCustomAttributes(typeof(ForeignKeyDeclarationAttribute), true).Select<object, ForeignKeyDeclarationAttribute>(a => (ForeignKeyDeclarationAttribute)a);
                        if (attrs.Any<ForeignKeyDeclarationAttribute>(attr => attr.ForeignType == parent.GetType() && attr.MemberName == fi.Name && attr.RelationQuantity != RelationQuantity.ManyOrZeroToManyOrZero))
                        {
                            IEnumerable<object> children = RetrieveOneToManyFKObjsFromDB(connectionString, parent, t, fi);
                            //for each child we need: 
                            //ensure identity (not to have several object with the same pk), 
                            //add it in this.instanciatedObjects, 
                            //mark it as ghost
                            List<object> checkedChildren = new List<object>();
                            foreach (object child in children)
                            {
                                object obj;
                                if (this.instanciatedObjects.TryGetValue(GetPrimaryKeyFromObj(child), out obj))
                                    checkedChildren.Add(obj);
                                else
                                {
                                    //if (IsObjTypeParent(child.GetType()))
                                    LazyLoadStatus.MarkAsGhost(child);
                                    checkedChildren.Add(child);
                                    this.instanciatedObjects.Add(GetPrimaryKeyFromObj(child), child);
                                }
                            }

                            MethodInfo addMethod = fi.FieldType.GetMethods().Where(m => m.Name == "Add" && m.GetParameters().Count() == 1).FirstOrDefault();
                            foreach (object child in checkedChildren)
                                addMethod.Invoke(fi.GetValue(parent), new object[] { child });
                        }
                    }
                }
                else
                {
                    t2 = fi.FieldType;
                    IEnumerable<Type> ts = parent.GetType().Assembly.GetTypes().Where<Type>(t1 => t2.IsAssignableFrom(t1));
                    foreach (Type t in ts)
                    {

                        IEnumerable<ForeignKeyDeclarationAttribute> attrs = t.GetCustomAttributes(typeof(ForeignKeyDeclarationAttribute), true).Select<object, ForeignKeyDeclarationAttribute>(a => (ForeignKeyDeclarationAttribute)a);
                        if (attrs.Any<ForeignKeyDeclarationAttribute>(attr => attr.ForeignType == parent.GetType() && attr.MemberName == fi.Name && attr.RelationQuantity != RelationQuantity.ManyOrZeroToManyOrZero))
                        {
                            object child = RetrieveOneToManyFKObjsFromDB(connectionString, parent, t, fi).FirstOrDefault();
                            if (child != null)
                            {
                                object obj;
                                if (this.instanciatedObjects.TryGetValue(GetPrimaryKeyFromObj(child), out obj))
                                    fi.SetValue(parent, obj);
                                else
                                {
                                    //if (IsObjTypeParent(child.GetType()))
                                    LazyLoadStatus.MarkAsGhost(child);
                                    this.instanciatedObjects.Add(GetPrimaryKeyFromObj(child), child);
                                    fi.SetValue(parent, child);
                                }
                            }
                        }
                    }
                }
            }

            IEnumerable<PropertyInfo> propertyInfos = parent.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
            foreach (PropertyInfo pi in propertyInfos)
                if (pi.CanRead && pi.CanWrite)
                {
                    Type t2;
                    if (pi.PropertyType.GetInterfaces().Any<Type>(i => i == typeof(IList))
                        && pi.PropertyType.GetInterfaces().Any<Type>(i => i == typeof(IEnumerable))
                        && pi.PropertyType.IsGenericType)
                    {
                        t2 = pi.PropertyType.GetGenericArguments().First<Type>();
                        IEnumerable<Type> ts = parent.GetType().Assembly.GetTypes().Where<Type>(t1 => t2.IsAssignableFrom(t1));
                        foreach (Type t in ts)
                        {
                            IEnumerable<ForeignKeyDeclarationAttribute> attrs = t.GetCustomAttributes(typeof(ForeignKeyDeclarationAttribute), true).Select<object, ForeignKeyDeclarationAttribute>(a => (ForeignKeyDeclarationAttribute)a);
                            if (attrs.Any<ForeignKeyDeclarationAttribute>(attr => attr.ForeignType == parent.GetType() && attr.MemberName == pi.Name && attr.RelationQuantity != RelationQuantity.ManyOrZeroToManyOrZero))
                            {
                                IEnumerable<object> children = RetrieveOneToManyFKObjsFromDB(connectionString, parent, t, pi);
                                List<object> checkedChildren = new List<object>();
                                foreach (object child in children)
                                {
                                    object obj;
                                    if (this.instanciatedObjects.TryGetValue(GetPrimaryKeyFromObj(child), out obj))
                                        checkedChildren.Add(obj);
                                    else
                                    {
                                        //if (IsObjTypeParent(child.GetType()))
                                        LazyLoadStatus.MarkAsGhost(child);
                                        checkedChildren.Add(child);
                                        this.instanciatedObjects.Add(GetPrimaryKeyFromObj(child), child);
                                    }
                                }

                                MethodInfo addMethod = pi.PropertyType.GetMethods().Where(m => m.Name == "Add" && m.GetParameters().Count() == 1).FirstOrDefault();
                                foreach (object child in checkedChildren)
                                    addMethod.Invoke(pi.GetValue(parent, null), new object[] { child });
                            }
                        }
                    }
                    else
                    {
                        t2 = pi.PropertyType;
                        IEnumerable<Type> ts = parent.GetType().Assembly.GetTypes().Where<Type>(t1 => t2.IsAssignableFrom(t1));
                        foreach (Type t in ts)
                        {

                            IEnumerable<ForeignKeyDeclarationAttribute> attrs = t.GetCustomAttributes(typeof(ForeignKeyDeclarationAttribute), true).Select<object, ForeignKeyDeclarationAttribute>(a => (ForeignKeyDeclarationAttribute)a);
                            if (attrs.Any<ForeignKeyDeclarationAttribute>(attr => attr.ForeignType == parent.GetType() && attr.MemberName == pi.Name && attr.RelationQuantity != RelationQuantity.ManyOrZeroToManyOrZero))
                            {
                                object child = RetrieveOneToManyFKObjsFromDB(connectionString, parent, t, pi).FirstOrDefault();
                                if (child != null)
                                {
                                    object obj;
                                    if (this.instanciatedObjects.TryGetValue(GetPrimaryKeyFromObj(child), out obj))
                                        pi.SetValue(parent, obj, null);
                                    else
                                    {
                                        //if (IsObjTypeParent(child.GetType()))
                                        LazyLoadStatus.MarkAsGhost(child);
                                        this.instanciatedObjects.Add(GetPrimaryKeyFromObj(child), child);
                                        pi.SetValue(parent, child, null);
                                    }
                                }
                            }
                        }
                    }
                }
        }

        private void InitBLLObjManyToManyFKObjects(string connectionString, object parent)
        {
            IEnumerable<ForeignKeyDeclarationAttribute> attrs =
                parent.GetType().GetCustomAttributes(typeof(ForeignKeyDeclarationAttribute), true)
                    .Select<object, ForeignKeyDeclarationAttribute>(a => (ForeignKeyDeclarationAttribute)a)
                    .Where<ForeignKeyDeclarationAttribute>(a => a.RelationQuantity == RelationQuantity.ManyOrZeroToManyOrZero);
            foreach (ForeignKeyDeclarationAttribute attr in attrs)
            {
                //get parent's member in which children are stored
                PropertyInfo propertyInfo = parent.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)
                    .Where<PropertyInfo>(pi => pi.CanRead && pi.CanWrite && String.Compare(pi.Name, attr.MemberName, StringComparison.OrdinalIgnoreCase) == 0)
                    .FirstOrDefault();

                FieldInfo fieldInfo = parent.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance)
                    .Where<FieldInfo>(fi => String.Compare(fi.Name, attr.MemberName, StringComparison.OrdinalIgnoreCase) == 0)
                    .FirstOrDefault();

                //retrieve children
                IEnumerable<object> children = new List<object>();
                if (fieldInfo != null)
                    children = RetrieveManyToManyFKObjsFromDB(connectionString, parent, attr.ForeignType, fieldInfo, attr.Ord);
                else if (propertyInfo != null)
                    children = RetrieveManyToManyFKObjsFromDB(connectionString, parent, attr.ForeignType, propertyInfo, attr.Ord);

                //for each child we need: 
                //ensure identity (not to have several object with the same pk), 
                //add it in this.instanciatedObjects, 
                //mark it as ghost
                List<object> checkedChildren = new List<object>();
                foreach (object child in children)
                {
                    object obj;
                    if (this.instanciatedObjects.TryGetValue(GetPrimaryKeyFromObj(child), out obj))
                        checkedChildren.Add(obj);
                    else
                    {
                        //if (IsObjTypeParent(child.GetType()))
                        LazyLoadStatus.MarkAsGhost(child);
                        checkedChildren.Add(child);
                        this.instanciatedObjects.Add(GetPrimaryKeyFromObj(child), child);
                    }
                }

                if (fieldInfo != null)
                {
                    MethodInfo addMethod = fieldInfo.FieldType.GetMethods().Where(m => m.Name == "Add" && m.GetParameters().Count() == 1).FirstOrDefault();
                    foreach (object child in checkedChildren)
                        addMethod.Invoke(fieldInfo.GetValue(parent), new object[] { child });
                }
                else if (propertyInfo != null)
                {
                    MethodInfo addMethod = propertyInfo.PropertyType.GetMethods().Where(m => m.Name == "Add" && m.GetParameters().Count() == 1).FirstOrDefault();
                    foreach (object child in checkedChildren)
                        addMethod.Invoke(propertyInfo.GetValue(parent, null), new object[] { child });
                }
            }

        }

        private IDictionary<int, IField> GetBLLObjTypePrimaryKeyFields(object bllObj)
        {
            Dictionary<int, IField> result = new Dictionary<int, IField>();
            IEnumerable<FieldInfo> fieldInfos = bllObj.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance);
            fieldInfos = fieldInfos.Where(fi => fi.GetCustomAttributes(typeof(PrimaryKeyAttribute), true).Count() > 0);
            foreach (FieldInfo fi in fieldInfos)
            {
                PrimaryKeyAttribute pka = (PrimaryKeyAttribute)fi.GetCustomAttributes(typeof(PrimaryKeyAttribute), true).First();
                string fieldName = (String.IsNullOrEmpty(pka.FieldName)) ? fi.Name : pka.FieldName;
                if (TypeAndRowDescriptorAux.GetInstance(this.dbmsType).IsResolvebleToDBMSType(fi.FieldType) == false)
                    throw new Exception("Types member attributed with PrimaryKeyAttribute have type that is unresolveble to dbms type.");
                int i = pka.Ord;
                object fieldValue = fi.GetValue(bllObj);
                result.Add(i, new Field(fi.FieldType, fieldName, fieldValue));
            }

            bool pkctor = false;
            foreach (ConstructorInfo ctor in bllObj.GetType().GetConstructors())
                if (ctor.GetCustomAttributes(typeof(PrimaryConstructorAttribute), true).Count() == 1)
                    pkctor = true;

            IEnumerable<PropertyInfo> propertyInfos = bllObj.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
            propertyInfos = propertyInfos.Where(pi => pi.GetCustomAttributes(typeof(PrimaryKeyAttribute), true).Count() > 0);
            foreach (PropertyInfo pi in propertyInfos)
                if (pi.CanRead && (pi.CanWrite || pkctor))
                {
                    PrimaryKeyAttribute pka = (PrimaryKeyAttribute)pi.GetCustomAttributes(typeof(PrimaryKeyAttribute), true).First();
                    string fieldName = (String.IsNullOrEmpty(pka.FieldName)) ? pi.Name : pka.FieldName;
                    if (TypeAndRowDescriptorAux.GetInstance(this.dbmsType).IsResolvebleToDBMSType(pi.PropertyType) == false)
                        throw new Exception("Types member attributed with PrimaryKeyAttribute have type that is unresolveble to dbms type.");
                    int i = pka.Ord;
                    object fieldValue = pi.GetValue(bllObj, null);
                    result.Add(i, new Field(pi.PropertyType, fieldName, fieldValue));
                }
            return result;
        }

        private IEnumerable<IField> GetFieldsFromObj(object bllObj)
        {
            //this method is used to store entity ("entity" from object and "relation" from db)
            //we store pk and fields in order to store "entity" and retrive fk from db just to add them to IRow
            //if we store IRow without fks DynORM will update table structure (remove fk in the table)
            List<IField> result = new List<IField>();

            IEnumerable<FieldInfo> fieldInfos = bllObj.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance);
            fieldInfos = fieldInfos.Where(fi => fi.GetCustomAttributes(typeof(FieldAttribute), true).Count() > 0);
            foreach (FieldInfo fi in fieldInfos)
            {
                FieldAttribute fa = (FieldAttribute)fi.GetCustomAttributes(typeof(FieldAttribute), true).First();
                string fieldName = (String.IsNullOrEmpty(fa.FieldName)) ? fi.Name : fa.FieldName;

                if (TypeAndRowDescriptorAux.GetInstance(this.dbmsType).IsResolvebleToDBMSType(fi.FieldType) == false)
                    throw new Exception("Types member attributed with PrimaryKeyAttribute have type that is unresolveble to dbms type.");
                object fieldValue = fi.GetValue(bllObj);

                result.Add(new Field(fi.FieldType, fieldName, fieldValue));
            }

            IEnumerable<PropertyInfo> propertyInfos = bllObj.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
            propertyInfos = propertyInfos.Where(pi => pi.GetCustomAttributes(typeof(FieldAttribute), true).Count() > 0);
            foreach (PropertyInfo pi in propertyInfos)
                if (pi.CanRead && pi.CanWrite)
                {
                    FieldAttribute fa = (FieldAttribute)pi.GetCustomAttributes(typeof(FieldAttribute), true).First();
                    string fieldName = (String.IsNullOrEmpty(fa.FieldName)) ? pi.Name : fa.FieldName;

                    if (TypeAndRowDescriptorAux.GetInstance(this.dbmsType).IsResolvebleToDBMSType(pi.PropertyType) == false)
                        throw new Exception("Types member attributed with PrimaryKeyAttribute have type that is unresolveble to dbms type.");
                    object fieldValue = pi.GetValue(bllObj, null);

                    result.Add(new Field(pi.PropertyType, fieldName, fieldValue));
                }

            return result;
        }

        private IEnumerable<IForeignKey> RetrieveForeignKeysFromDB(string connectionString, object bllObj)
        {
            //this method is used to store entity ("entity" from object and "relation" from db)
            //we store pk and fields in order to store "entity" and retrive fk from db just to add them to IRow
            //if we store IRow without fks DynORM will update table structure (remove fk in the table)
            if (IsObjTypeChild(bllObj.GetType()))
            {
                IPrimaryKey pk = GetPrimaryKeyFromObj(bllObj);
                IRow row = RowMapper.RowRead(connectionString, this.dbmsType, pk);
                if (row != null) //if (IsObjStoredInDB(bllObj))
                    //if object is not new retrieve FKs from DB
                    return row.ForeignKeys;
                else
                {
                    List<IForeignKey> result = new List<IForeignKey>();
                    //if object is new init FKs with nulls and empty guids (for related fields)
                    IRowDescriptor rd = TypeAndRowDescriptorAux.GetInstance(this.dbmsType).GetBLLObjTypeRowDescriptor(bllObj.GetType());
                    foreach (IForeignKeyDescriptor fkd in rd.ForeignKeyDescriptors)
                    {
                        Dictionary<int, IField> fields = new Dictionary<int, IField>();
                        Dictionary<int, IField> relatedFields = new Dictionary<int, IField>();
                        foreach (KeyValuePair<int, IFieldDescriptor> kv in fkd.FieldDescriptors)
                            fields.Add(kv.Key, new Field(kv.Value, null));
                        foreach (KeyValuePair<int, IFieldDescriptor> kv in fkd.RelatedFieldDescriptors)
                            relatedFields.Add(kv.Key, new Field(kv.Value, Guid.Empty));
                        result.Add(new ForeignKey(fkd.TableName, fields, fkd.RelatedTableName, relatedFields, fkd.Name));
                    }
                    return result;
                }
            }
            else
                return new List<IForeignKey>();
        }

        private IPrimaryKey RetrievePrimaryKeyFromDB(string connectionString, object bllObj)
        {
            //this method is used to store relation ("entity" from db and "relation" from object)
            //we store fks in order to store "relations" and retrive pk and fields from db just to add them to IRow

            //although RetrieveRow(GetPKFromObj(bllObj)) can return null this method is used in WriteChildsFKs which is invoked when entity has been stored and relation is storing
            //return GetPrimaryKeyFromObj(bllObj);
            return RowMapper.RowRead(connectionString, this.dbmsType, GetPrimaryKeyFromObj(bllObj)).PrimaryKey; //usually there is no need in the roundtrip to db though
        }

        private IEnumerable<IField> RetrieveFieldsFromDB(string connectionString, object bllObj)
        {
            //this method is used to store relation ("entity" from db and "relation" from object)
            //we store fks in order to store "relations" and retrive pk and fields from db just to add them to IRow

            //although RetrieveRow(GetPKFromObj(bllObj)) can return null this method is used in WriteChildsFKs which is invoked when entity has been stored and relation is storing
            return RowMapper.RowRead(connectionString, this.dbmsType, GetPrimaryKeyFromObj(bllObj)).Fields;
        }
        
        private IForeignKey GetForeignKeyFromObj(object parent, object child, int ord)
        {
            //this method is used to store relation ("entity" from db and "relation" from object)
            //we store fks in order to store "relations" and retrive pk and fields from db just to add them to IRow
            string tableName = TypeAndRowDescriptorAux.GetBLLObjTypeTableName(child.GetType());
            string relatedTableName = TypeAndRowDescriptorAux.GetBLLObjTypeTableName(parent.GetType());
            IDictionary<int, IField> fields = new Dictionary<int, IField>();
            IDictionary<int, IFieldDescriptor> unrenamedFields = TypeAndRowDescriptorAux.GetInstance(this.dbmsType).GetBLLObjTypePrimaryKeyFieldDescriptors(parent.GetType());
            IDictionary<int, IField> relatedFields = GetBLLObjTypePrimaryKeyFields(parent);

            //using reflection find FK in child (process ForeignKeyDeclarationAttribute)
            IEnumerable<ForeignKeyDeclarationAttribute> fkdas = child.GetType().GetCustomAttributes(typeof(ForeignKeyDeclarationAttribute), true).Select<object, ForeignKeyDeclarationAttribute>(fka => (ForeignKeyDeclarationAttribute)fka);
            ForeignKeyDeclarationAttribute fkda = fkdas.Where<ForeignKeyDeclarationAttribute>(fka => fka.ForeignType == parent.GetType() && fka.Ord == ord && fka.RelationQuantity != RelationQuantity.ManyOrZeroToManyOrZero).First();

            foreach (KeyValuePair<int, IFieldDescriptor> kv in unrenamedFields)
                if (fkda.RelationQuantity == RelationQuantity.OneOrZeroToManyOrZero || fkda.RelationQuantity == RelationQuantity.OneOrZeroToOneOrZero)
                    fields.Add(kv.Key, new Field(TypeAndRowDescriptorAux.GetCLRNullableType(kv.Value.FieldType), String.Format("{0}_{1}_{2}_{3}_{4}", tableName, relatedTableName, ForeignKeyDescriptor.PredefinedFieldNameForForeignIDs, ord, kv.Key), relatedFields[kv.Key].FieldValue));
                else if (fkda.RelationQuantity == RelationQuantity.OneToManyOrZero || fkda.RelationQuantity == RelationQuantity.OneToOneOrZero)
                    fields.Add(kv.Key, new Field(kv.Value.FieldType, String.Format("{0}_{1}_{2}_{3}_{4}", tableName, relatedTableName, ForeignKeyDescriptor.PredefinedFieldNameForForeignIDs, ord, kv.Key), relatedFields[kv.Key].FieldValue));

            string name = String.Format("{0}_{1}_FK_{2}", tableName, relatedTableName, ord);
            return new ForeignKey(tableName, fields, relatedTableName, relatedFields, name);
        }



        private void DeleteOneToManyRelation(object parent, object child, int ord, string connectionString)
        {
            //this method is similar to WriteParentsPKsandFields
            if (RowDescriptorMapper.IsExistsInDB(TypeAndRowDescriptorAux.GetBLLObjTypeTableName(child.GetType()), connectionString, this.dbmsType) == false)
                return;

            IEnumerable<IForeignKey> fks = RetrieveForeignKeysFromDBWithResetOne(parent, child, ord, connectionString);
            IPrimaryKey pk = GetPrimaryKeyFromObj(child);
            if (RowMapper.RowRead(connectionString, this.dbmsType, pk) == null)
                return;
            IEnumerable<IField> fs = GetFieldsFromObj(child);
            IRow row = new Row(fs, fks.Union<IRelation>(new List<IRelation> { pk }));

            RowMapper.RowWrite(connectionString, this.dbmsType, row);
        }

        private bool ForeignEquality(IForeignKey fk, IPrimaryKey pk)
        {
            IPrimaryKey surrogate_pk = new PrimaryKey(fk.RelatedTableName, fk.RelatedFields, pk.Name);
            if (surrogate_pk.Equals(pk))
                return true;
            else
                return false;
        }

        private IEnumerable<IForeignKey> RetrieveForeignKeysFromDBWithResetOne(object parent, object child, int ord, string connectionString)
        {
            //erase all the relations between parent and child objects with given Ord 
            //Ord property of ForeignKeyDeclarationAttribute reflected in DB in FK name (and its FK fields' names)
            List<IForeignKey> result = new List<IForeignKey>();

            IPrimaryKey pk = GetPrimaryKeyFromObj(parent);
            string tableName = TypeAndRowDescriptorAux.GetBLLObjTypeTableName(child.GetType());
            string relatedTableName = TypeAndRowDescriptorAux.GetBLLObjTypeTableName(parent.GetType());
            string name = String.Format("{0}_{1}_FK_{2}", tableName, relatedTableName, ord);

            IPrimaryKey child_pk = GetPrimaryKeyFromObj(child);
            IRow child_row = RowMapper.RowRead(connectionString, this.dbmsType, child_pk);

            ForeignKeyDeclarationAttribute fkda = child.GetType().GetCustomAttributes(typeof(ForeignKeyDeclarationAttribute), true).Select(o => (ForeignKeyDeclarationAttribute)o).Where(fkda1 => fkda1.Ord == ord && fkda1.RelationQuantity != RelationQuantity.ManyOrZeroToManyOrZero).First();

            if (child_row != null)
                foreach (IForeignKey fk in child_row.ForeignKeys)
                {
                    if (String.Compare(fk.TableName, tableName, StringComparison.InvariantCultureIgnoreCase) == 0
                        && String.Compare(fk.RelatedTableName, relatedTableName, StringComparison.InvariantCultureIgnoreCase) == 0
                        && ForeignEquality(fk, pk)
                        && String.Compare(fk.Name, name, StringComparison.InvariantCultureIgnoreCase) == 0) //without this condition we'll delete all the refeerences between these two tables if there are several
                    //init FKs with nulls and empty guids (for related fields)
                    {
                        Dictionary<int, IField> fields = new Dictionary<int, IField>();
                        Dictionary<int, IField> relatedFields = new Dictionary<int, IField>();
                        foreach (KeyValuePair<int, IField> kv in fk.Fields)
                            if (fkda.RelationQuantity == RelationQuantity.OneToManyOrZero || fkda.RelationQuantity == RelationQuantity.OneToOneOrZero)
                                //we can get an exception when we store nulls it in case of RelationQuantity.OneToManyOrZero or RelationQuantity.OneToOneOrZero
                                fields.Add(kv.Key, new Field((IFieldDescriptor)kv.Value.GetDescriptor(), Guid.Empty));
                            else if (fkda.RelationQuantity == RelationQuantity.OneOrZeroToManyOrZero || fkda.RelationQuantity == RelationQuantity.OneOrZeroToOneOrZero)
                                //we can get an exception when we store nulls it in case of RelationQuantity.OneToManyOrZero or RelationQuantity.OneToOneOrZero
                                fields.Add(kv.Key, new Field((IFieldDescriptor)kv.Value.GetDescriptor(), null));
                        foreach (KeyValuePair<int, IField> kv in fk.RelatedFields)
                            relatedFields.Add(kv.Key, new Field((IFieldDescriptor)kv.Value.GetDescriptor(), Guid.Empty));
                        result.Add(new ForeignKey(fk.TableName, fields, fk.RelatedTableName, relatedFields, fk.Name));
                    }
                    else
                    {
                        result.Add(fk);
                    }
                }

            return result;
        }

        private void DeleteManyToManyRelation(object parent, object child, int ord, string connectionString)
        {
            //this method is very similar to StoreManyToManyChildFKs()
            string linkTableName = String.Format("{0}_{1}_{2}", TypeAndRowDescriptorAux.GetBLLObjTypeTableName(parent.GetType()), TypeAndRowDescriptorAux.GetBLLObjTypeTableName(child.GetType()), ord);
            if (RowDescriptorMapper.IsExistsInDB(linkTableName, connectionString, this.dbmsType) == false)
            {
                linkTableName = String.Format("{0}_{1}_{2}", TypeAndRowDescriptorAux.GetBLLObjTypeTableName(child.GetType()), TypeAndRowDescriptorAux.GetBLLObjTypeTableName(parent.GetType()), ord);
                if (RowDescriptorMapper.IsExistsInDB(linkTableName, connectionString, this.dbmsType) == false)
                    return;
            }

            string FKToParentName = String.Format("{0}_{1}_FK", linkTableName, TypeAndRowDescriptorAux.GetBLLObjTypeTableName(parent.GetType()));
            string FKToChildName = String.Format("{0}_{1}_FK", linkTableName, TypeAndRowDescriptorAux.GetBLLObjTypeTableName(child.GetType()));
            //construct the first fk
            IPrimaryKey parentPK = GetPrimaryKeyFromObj(parent);
            IDictionary<int, IField> fkToParentFields = new Dictionary<int, IField>();
            foreach (KeyValuePair<int, IField> kv in parentPK.Fields)
                fkToParentFields.Add(kv.Key, new Field(kv.Value.FieldType,
                                                        String.Format("{0}_{1}_{2}_{3}", linkTableName, TypeAndRowDescriptorAux.GetBLLObjTypeTableName(parent.GetType()), ForeignKeyDescriptor.PredefinedFieldNameForForeignIDs, kv.Key),
                                                        kv.Value.FieldValue));
            IForeignKey fkToParent = new ForeignKey(linkTableName, fkToParentFields, TypeAndRowDescriptorAux.GetBLLObjTypeTableName(parent.GetType()), parentPK.Fields, FKToParentName);

            //construct the second fk
            IPrimaryKey childPK = GetPrimaryKeyFromObj(child);
            IDictionary<int, IField> fkToChildFields = new Dictionary<int, IField>();
            foreach (KeyValuePair<int, IField> kv in childPK.Fields)
                fkToChildFields.Add(kv.Key, new Field(kv.Value.FieldType,
                                                        String.Format("{0}_{1}_{2}_{3}", linkTableName, TypeAndRowDescriptorAux.GetBLLObjTypeTableName(child.GetType()), ForeignKeyDescriptor.PredefinedFieldNameForForeignIDs, kv.Key),
                                                        kv.Value.FieldValue));
            IForeignKey fkToChild = new ForeignKey(linkTableName, fkToChildFields, TypeAndRowDescriptorAux.GetBLLObjTypeTableName(child.GetType()), childPK.Fields, FKToChildName);

            //construct pk
            IDictionary<int, IField> pkFields = new Dictionary<int, IField>();
            int i = 0;
            foreach (KeyValuePair<int, IField> kv in fkToParentFields.OrderBy<KeyValuePair<int, IField>, int>(kv1 => kv1.Key))
                pkFields.Add(i++, kv.Value);
            foreach (KeyValuePair<int, IField> kv in fkToChildFields.OrderBy<KeyValuePair<int, IField>, int>(kv1 => kv1.Key))
                pkFields.Add(i++, kv.Value);
            IPrimaryKey pk = new PrimaryKey(linkTableName, pkFields, String.Format("{0}_PK", linkTableName));
            RowMapper.RowDelete(connectionString, this.dbmsType, pk);
        }



        public object InitPKsandFields(Type bllObjType, IRow row, bool checkStructure)
        {
        //this method is used for "entity" loading
            object result = null;
            if (checkStructure)
                if (TypeAndRowDescriptorAux.GetInstance(this.dbmsType).CheckBLLObjTypeAndRowDescCorrespondence(bllObjType, (IRowDescriptor)row.GetDescriptor()) == false)
                    throw new Exception("Type and IRowDescriptor do not corresponds each other.");

            bool pkctor = false;
            foreach (ConstructorInfo ctor in bllObjType.GetConstructors())
                if (ctor.GetCustomAttributes(typeof(PrimaryConstructorAttribute), true).Count() == 1)
                    pkctor = true;

            if (pkctor)
            {
                object[] bllObjIDs = new object[row.PrimaryKey.Fields.Count];
                for (int i = 0; i < row.PrimaryKey.Fields.Count; i++)
                    bllObjIDs[i] = row.PrimaryKey.Fields[i].FieldValue;
                result = Activator.CreateInstance(bllObjType, bllObjIDs);
            }
            else
            {
                result = Activator.CreateInstance(bllObjType);
                InitBLLObjPrimaryKey(result, row);
            }
            InitBLLObjFields(result, row);

            return result;
        }

        public object CreateBLLObj(Type bllObjType, string connectionString, params object[] bllObjIDs)
        {
            //if (TypeAndRowDescriptorAux.GetInstance(dbmsType).IsExistsInDB(bllObjType, connectionString))
            IRowDescriptor rd = TypeAndRowDescriptorAux.GetInstance(dbmsType).GetBLLObjTypeRowDescriptor(bllObjType);
            if (RowDescriptorMapper.IsExistsInDB(rd, connectionString, dbmsType) == false)
                return null;

            IPrimaryKey pk = new PrimaryKey(rd.PrimaryKeyDescriptor, bllObjIDs);
            IRow row = RowMapper.RowRead(connectionString, this.dbmsType, pk);
            if (row == null)
                return null;

            return InitPKsandFields(bllObjType, row, true);
        }

        public IEnumerable<object> CreateBLLObjs(Type bllObjType, string connectionString, IEnumerable<BLLComparison> predicate)
        {
            List<object> result = new List<object>();

            string tableName = TypeAndRowDescriptorAux.GetBLLObjTypeTableName(bllObjType);

            List<IORMComparison> orm_predicate = new List<IORMComparison>();
            foreach (BLLComparison c in predicate)
                orm_predicate.Add(PredicateConverter.BLLToORMComparison(c));

            IEnumerable<IRow> rows;
            if (orm_predicate.Count() > 0)
                rows = RowMapper.RowsRead(connectionString, dbmsType, tableName, orm_predicate);
            else
                rows = RowMapper.RowsRead(connectionString, dbmsType, tableName);

            if (rows.Count() == 0)
                return result;

            //check structure only once for all the rows
            if (TypeAndRowDescriptorAux.GetInstance(this.dbmsType).CheckBLLObjTypeAndRowDescCorrespondence(bllObjType, (IRowDescriptor)rows.First().GetDescriptor()) == false)
                throw new Exception("Type and IRowDescriptor do not corresponds each other.");

            //call InitPKsandFields with false for checkStrusture cause it's enough to check structure once for all the rows
            foreach (IRow r in rows)
                result.Add(ObjectAndRowAux.GetInstance(dbmsType).InitPKsandFields(bllObjType, r, false));

            return result;
        }

        public void InitBLLObjFKObjects(string connectionString, object parent)
        {
            InitBLLObjOneToManyFKObjects(connectionString, parent);
            InitBLLObjManyToManyFKObjects(connectionString, parent);
        }

        public IPrimaryKey GetPrimaryKeyFromObj(object bllObj)
        {
            //this method is used to store entity ("entity" from object and "relation" from db)
            //we store pk and fields in order to store "entity" and retrive fk from db just to add them to IRow
            //if we store IRow without fks DynORM will update table structure (remove fk in the table)
            IPrimaryKey result = null;
            string tableName = TypeAndRowDescriptorAux.GetBLLObjTypeTableName(bllObj.GetType());
            IDictionary<int, IField> fields = GetBLLObjTypePrimaryKeyFields(bllObj);

            string pkname = String.Format("{0}_PK", tableName);

            result = new PrimaryKey(tableName, fields, pkname);
            return result;
        }

        public bool IsObjExistsInDB(string connectionString, object bllObj)
        {
            if (RowMapper.RowRead(connectionString, this.dbmsType, GetPrimaryKeyFromObj(bllObj)) == null)
                return false;
            else
                return true;
        }

        public void StoreStructure(string connectionString, Type bllObjType)
        {
            IRowDescriptor rd = TypeAndRowDescriptorAux.GetInstance(this.dbmsType).GetBLLObjTypeRowDescriptor(bllObjType);
            RowDescriptorMapper.Store(connectionString, this.dbmsType, rd);
        }

        public void StoreParentsPKsandFields(string connectionString, object parent, bool updateStructure)
        {
            IRowDescriptor rd = TypeAndRowDescriptorAux.GetInstance(this.dbmsType).GetBLLObjTypeRowDescriptor(parent.GetType());
            if (RowDescriptorMapper.IsExistsInDB(rd, connectionString, this.dbmsType) == false || updateStructure == true)
                RowDescriptorMapper.Store(connectionString, this.dbmsType, rd);

            //it works here:
            //1) with a type that is not related to any other types
            //2) with a type that is parent (if (IsObjTypeParent()) is true)
            //3) with a type that is child (if (IsObjTypeChild()) is true)
            //4) with a type that is parent and child
            //store pk and fields (and possibly FKs (retrieve FK in order to have a complete Row (and its RowDescriptor), otherwise DynORM will correct DB table structure))
            //so we store pk nd fields but compile the whole stucture

            IEnumerable<IForeignKey> fks = RetrieveForeignKeysFromDB(connectionString, parent);
            IPrimaryKey pk = GetPrimaryKeyFromObj(parent);
            IEnumerable<IField> fs = GetFieldsFromObj(parent);
            IRow row = new Row(fs, fks.Union<IRelation>(new List<IRelation> { pk }));

            RowMapper.RowWrite(connectionString, this.dbmsType, row);
        }

        public void StoreOneToManyChildFKs(object parent, object child, int ord, string connectionString)
        {
            //update the relation (fk fields values) between the parent and child objects with given Ord number (property of ForeignKeyDeclarationAttribute)

            //in this method we merge modified (in-memory) parts of table ("relation" to the parent) with 
            //parts in db ("entity" and "relations" to other parents) and store it in db.
            //we can be sure that object does exists in db because storing of "relations" is always after the storing of "entities" in this library.
            ForeignKeyDeclarationAttribute fkda =
                    child.GetType().GetCustomAttributes(typeof(ForeignKeyDeclarationAttribute), true)
                        .Select(ca => (ForeignKeyDeclarationAttribute)ca)
                        .Where(fkda1 => fkda1.ForeignType == parent.GetType() && fkda1.RelationQuantity != RelationQuantity.ManyOrZeroToManyOrZero && fkda1.Ord == ord)
                        .First();
            IForeignKey inMemoryfk = GetForeignKeyFromObj(parent, child, fkda.Ord);

            //Ord property of ForeignKeyDeclarationAttribute reflected in DB only in FK name, so we use ForeignKeyDescriptor equality
            IEnumerable<IForeignKey> inDBfks = RetrieveForeignKeysFromDB(connectionString, child).Where<IForeignKey>(fk => !fk.GetDescriptor().Equals(inMemoryfk.GetDescriptor()));
            List<IForeignKey> fksToStore = new List<IForeignKey>();
            foreach (IForeignKey inDBfk in inDBfks)
                fksToStore.Add(inDBfk);
            fksToStore.Add(inMemoryfk);
            //retrieve PK and Fields
            //we supposed to get pk and fields from db but to avoid extra roundtrip we take this data from object. we can be sure they are in sync because 
            //the step of storing "relations" is always after the step of saving "entities" in the library
            IPrimaryKey pk = GetPrimaryKeyFromObj(child); //RetrievePrimaryKeyFromDB ??
            IEnumerable<IField> fs = GetFieldsFromObj(child);
            //construct Row out of all the stuff
            IRow row = new Row(fs, fksToStore.Union<IRelation>(new List<IRelation> { pk }));

            //IRowDescriptor rd = TypeAndRowDescriptorAux.GetInstance(dbmsType).GetBLLObjTypeRowDescriptor(child.GetType());
            IRowDescriptor rd = (IRowDescriptor)row.GetDescriptor();
            if (!RowDescriptorMapper.IsExistsInDB(rd, connectionString, dbmsType))
                RowDescriptorMapper.Store(connectionString, this.dbmsType, rd);

            RowMapper.RowWrite(connectionString, this.dbmsType, row);
        }

        public void StoreManyToManyChildFKs(object parent, object child, int ord, string connectionString)
        {
            //we can be sure that child object does exists in db because storing of "relations" is always after the storing of "entities".
            //we need to store records in the link-table. the link table consists out of two fks and one pk which is declared on the two fks


            //restore it in order to update structure (for example, the second fk may have not been created 
            //we create link entity when we create the first participant of many-to-many relation
            //and in the link entity there is only one fk at the moment (to the first entity). when we create 
            //the second participant of the many-to-many relation we add in the link entity the second fk)

            
            string linkTableName = String.Format("{0}_{1}_{2}", TypeAndRowDescriptorAux.GetBLLObjTypeTableName(parent.GetType()), TypeAndRowDescriptorAux.GetBLLObjTypeTableName(child.GetType()), ord);
            bool childFirst = false;
            if (RowDescriptorMapper.IsExistsInDB(linkTableName, connectionString, this.dbmsType) == false)
            {
                linkTableName = String.Format("{0}_{1}_{2}", TypeAndRowDescriptorAux.GetBLLObjTypeTableName(child.GetType()), TypeAndRowDescriptorAux.GetBLLObjTypeTableName(parent.GetType()), ord);
                childFirst = true;
            }
            
            
            string FKToParentName = String.Format("{0}_{1}_FK", linkTableName, TypeAndRowDescriptorAux.GetBLLObjTypeTableName(parent.GetType()));
            string FKToChildName = String.Format("{0}_{1}_FK", linkTableName, TypeAndRowDescriptorAux.GetBLLObjTypeTableName(child.GetType()));
            //construct the first fk
            IPrimaryKey parentPK = GetPrimaryKeyFromObj(parent);
            IDictionary<int, IField> fkToParentFields = new Dictionary<int, IField>();
            foreach (KeyValuePair<int, IField> kv in parentPK.Fields)
                fkToParentFields.Add(kv.Key, new Field(kv.Value.FieldType,
                                                        String.Format("{0}_{1}_{2}_{3}", linkTableName, TypeAndRowDescriptorAux.GetBLLObjTypeTableName(parent.GetType()), ForeignKeyDescriptor.PredefinedFieldNameForForeignIDs, kv.Key),
                                                        kv.Value.FieldValue));
            IForeignKey fkToParent = new ForeignKey(linkTableName, fkToParentFields, TypeAndRowDescriptorAux.GetBLLObjTypeTableName(parent.GetType()), parentPK.Fields, FKToParentName);

            //construct the second fk
            IPrimaryKey childPK = GetPrimaryKeyFromObj(child);
            IDictionary<int, IField> fkToChildFields = new Dictionary<int, IField>();
            foreach (KeyValuePair<int, IField> kv in childPK.Fields)
                fkToChildFields.Add(kv.Key, new Field(kv.Value.FieldType,
                                                        String.Format("{0}_{1}_{2}_{3}", linkTableName, TypeAndRowDescriptorAux.GetBLLObjTypeTableName(child.GetType()), ForeignKeyDescriptor.PredefinedFieldNameForForeignIDs, kv.Key),
                                                        kv.Value.FieldValue));
            IForeignKey fkToChild = new ForeignKey(linkTableName, fkToChildFields, TypeAndRowDescriptorAux.GetBLLObjTypeTableName(child.GetType()), childPK.Fields, FKToChildName);

            //construct pk
            IDictionary<int, IField> pkFields = new Dictionary<int, IField>();
            int i = 0;
            //in order to make odrer of fields in pk the same (independently of which participant on many-to-many 
            //relation we consider first). otherwise pk descriptors won't be equal and DynORM will be modifing pk declaration 
            //with every store in the link entity
            if (childFirst)
            { 
                foreach (KeyValuePair<int, IField> kv in fkToParentFields.OrderBy<KeyValuePair<int, IField>, int>(kv1 => kv1.Key))
                    pkFields.Add(i++, kv.Value);
                foreach (KeyValuePair<int, IField> kv in fkToChildFields.OrderBy<KeyValuePair<int, IField>, int>(kv1 => kv1.Key))
                    pkFields.Add(i++, kv.Value);
            }
            else
            { 
                foreach (KeyValuePair<int, IField> kv in fkToChildFields.OrderBy<KeyValuePair<int, IField>, int>(kv1 => kv1.Key))
                    pkFields.Add(i++, kv.Value);
                foreach (KeyValuePair<int, IField> kv in fkToParentFields.OrderBy<KeyValuePair<int, IField>, int>(kv1 => kv1.Key))
                    pkFields.Add(i++, kv.Value);
            }
            IPrimaryKey pk = new PrimaryKey(linkTableName, pkFields, String.Format("{0}_PK", linkTableName));
            //construct and store row
            IRow row = new Row(pk, new List<IField>(), new List<IForeignKey>() { fkToParent, fkToChild });
            IRowDescriptor rd = (IRowDescriptor)row.GetDescriptor();
            //if (RowDescriptorMapper.IsExistsInDB(rd, connectionString, dbmsType) == false)
            RowDescriptorMapper.Store(connectionString, this.dbmsType, rd); 
            RowMapper.RowWrite(connectionString, this.dbmsType, row);
        }

        public void StoreChildren(object parent, string connectionString)
        {
            IEnumerable<Tuple<int, object, RelationQuantity>> oneToManyChildren = GetOneToManyChildsFromObj(parent);
            foreach (Tuple<int, object, RelationQuantity> t in oneToManyChildren)
                StoreOneToManyChildFKs(parent, t.Item2, t.Item1, connectionString);
            IEnumerable<Tuple<int, object, RelationQuantity>> manyToManyChildren = GetManyToManyChildsFromObj(parent);
            foreach (Tuple<int, object, RelationQuantity> t in manyToManyChildren)
                StoreManyToManyChildFKs(parent, t.Item2, t.Item1, connectionString);
        }

        public void DeleteAllRelation(object bllObj, string connectionString)
        {
            IEnumerable<Tuple<int, object, RelationQuantity>> oneToManyChildren = ObjectAndRowAux.GetOneToManyChildsFromObj(bllObj);
            foreach (Tuple<int, object, RelationQuantity> t in oneToManyChildren)
                DeleteOneToManyRelation(bllObj, t.Item2, t.Item1, connectionString);
            IEnumerable<Tuple<int, object, RelationQuantity>> manyToManyChildren = ObjectAndRowAux.GetManyToManyChildsFromObj(bllObj);
            foreach (Tuple<int, object, RelationQuantity> t in manyToManyChildren)
                DeleteManyToManyRelation(bllObj, t.Item2, t.Item1, connectionString);
        }

        public void DeleteBLLObj(object bllObj, string connectionString)
        {
            IRowDescriptor rd = TypeAndRowDescriptorAux.GetInstance(this.dbmsType).GetBLLObjTypeRowDescriptor(bllObj.GetType());
            if (RowDescriptorMapper.IsExistsInDB(rd, connectionString, this.dbmsType) == false || IsObjExistsInDB(connectionString, bllObj) == false)
                return;

            IPrimaryKey pk = GetPrimaryKeyFromObj(bllObj);
            RowMapper.RowDelete(connectionString, this.dbmsType, pk);
        }

    }
}
